<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from 'three';
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { ref, onMounted } from "vue";
// @ts-ignore
import testPng from '@/assets/test.png'
import {BufferAttribute} from "three";

/*
 UV映射是一种将二维纹理映射到三维模型表面的技术。
 在这个过程中，3D模型上的每个顶点都会被赋予一个二维坐标（U, V）。
 U和V分别表示纹理坐标的水平和垂直方向。
 这些坐标用于将纹理图像上的像素与模型表面上的点进行对应。
 通过UV映射，我们可以在模型上精确地控制纹理的位置和方向。

 顶点UV坐标可以在0~1.0之间任意取值，纹理贴图左下角对应的UV坐标是(0,0)，右上角对应的坐标(1,1)。
  0, 0, // 图片左下角
  1, 0, // 图片右下角
  1, 1, // 图片右上角
  0, 1, // 图片左上角

 顶点位置（Position）表示3D模型中每个顶点的空间坐标（x, y, z）。
 顶点位置是三维坐标，描述了一个顶点在三维空间中的位置，用于确定模型在场景中的形状，
 UV坐标则表示该顶点在纹理上的二维坐标（U, V）。
 UV坐标是二维坐标，描述了一个顶点在纹理图像上的位置，用于确定纹理在模型上的分布。
*/

// 画布
const canvas = ref<any>('');
// 场景
const scene = new THREE.Scene();
// 纹理贴图
const texture = new THREE.TextureLoader().load(testPng);
// 材质
const material = new THREE.MeshLambertMaterial({
  map: texture, // 纹理贴图 -
                // 材质的颜色贴图属性.map设置后，模型会从纹理贴图上采集像素值，这时候一般来说不需要再设置材质颜色.color
                // .map贴图之所以称之为颜色贴图就是因为网格模型会获得颜色贴图的颜色值RGB
  side: THREE.DoubleSide, // 双面都展示
});
// 几何体
//BoxGeometry：长方体
const box = new THREE.BoxGeometry(100, 100, 100);
console.log('box uv: ', box.attributes.uv);

// SphereGeometry：球体
const sphere = new THREE.SphereGeometry(50);
// CylinderGeometry：圆柱
const cylinder = new THREE.CylinderGeometry(50,50,100);
// PlaneGeometry：矩形平面
const plane = new THREE.PlaneGeometry(100,50);
// CircleGeometry：圆形平面
const circle = new THREE.CircleGeometry(50);
// 自定义几何体
const custom = new THREE.BufferGeometry();
// 设置自定义几何体顶点属性
const vertices = new Float32Array([
  200, 0, 200, // 顶点1坐标
  250, 0, 200, // 顶点2坐标
  200, 100, 200, // 顶点3坐标

  200, 0, 200, // 顶点4坐标
  200, 0, 250, // 顶点5坐标
  200, 100, 200, // 顶点6坐标

  200, 100, 200, // 顶点7坐标 与 顶点3坐标 重合
  200, 0, 250, // 顶点8坐标 与 顶点5坐标 重合
  250, 0, 200, // 顶点9坐标 与 顶点6坐标 重合

  200, 100, 200, // 顶点10坐标 与 顶点3，顶点7 坐标 重合
  200, 0, 200, // 顶点11坐标 与 顶点4，顶点1 坐标 重合
  200, 0, 250, // 顶点12坐标 与 顶点5，顶点8 坐标 重合
]);
const attributes = new BufferAttribute(vertices, 3);
custom.attributes.position = attributes;
// 设置法线 - 因为这里用的是受光照影响的材质
const normalVertices = new Float32Array([
  0, 0, 1, // 顶点1法线坐标
  0, 0, 1, // 顶点2法线坐标
  0, 0, 1, // 顶点3法线坐标

  0, 0, 1, // 顶点4法线坐标
  0, 0, 1, // 顶点5法线坐标
  0, 0, 1, // 顶点6法线坐标

  0, 0, 1, // 顶点7法线坐标
  0, 0, 1, // 顶点8法线坐标
  0, 0, 1, // 顶点9法线坐标

  0, 0, 1, // 顶点10法线坐标
  0, 0, 1, // 顶点11法线坐标
  0, 0, 1, // 顶点12法线坐标
]);
custom.attributes.normal = new THREE.BufferAttribute(normalVertices, 3);
// 如果不手动设置UV坐标，那么这个地方的材质将不会生效，纹理贴图也不会正常展示
// 而且这里的UV坐标，是
const uv = new Float32Array([
  0, 0, // 图片左下角 - 顶点1的UV坐标
  1, 0, // 图片右下角 - 顶点2的UV坐标
  1, 1, // 图片右上角 - 顶点3的UV坐标

  0, 1, // 图片左上角 - 顶点4的UV坐标
  0, 0, // 图片左下角 - 顶点5的UV坐标
  1, 0, // 图片右下角 - 顶点6的UV坐标

  1, 1, // 图片右上角 - 顶点7的UV坐标
  0, 1, // 图片左上角 - 顶点8的UV坐标
  0, 0, // 图片左下角 - 顶点9的UV坐标

  1, 0, // 图片右下角 - 顶点10的UV坐标
  1, 1, // 图片右上角 - 顶点11的UV坐标
  0, 1, // 图片左上角 - 顶点12的UV坐标
]);
custom.attributes.uv = new BufferAttribute(uv, 2);
// 自定义几何体网格模型Mesh
const customMesh = new THREE.Mesh(custom, material);
scene.add(customMesh);

// 网格模型 mesh
const boxMesh = new THREE.Mesh(box, material);
boxMesh.position.set(0, 0, 0);

const sphereMesh = new THREE.Mesh(sphere, material);
sphereMesh.position.set(150, 0, 0);

const cylinderMesh = new THREE.Mesh(cylinder, material);
cylinderMesh.position.set(300, 0, 0);

const planeMesh = new THREE.Mesh(plane, material);
planeMesh.position.set(0, 0, 200);

const circleMesh = new THREE.Mesh(circle, material);
circleMesh.position.set(0, 0, 300);

scene.add(boxMesh, sphereMesh, cylinderMesh, planeMesh, circleMesh);

// 辅助观察的坐标点
const axesHelper = new THREE.AxesHelper(1000);
scene.add(axesHelper);
// 光源
// 平行光 - DirectionalLight
const directionalLight = new THREE.DirectionalLight(0xffffff);
scene.add(directionalLight);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 4000);
camera.position.set(400, 200, 300);
camera.lookAt(0, 0, 0);
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xffffff);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// 渲染函数
const render = () => {
  renderer.render(scene, camera);
  requestAnimationFrame(render);
};
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
orbitControl.update();

onMounted(() => {
  canvas.value.appendChild(renderer.domElement);
  render();
});

</script>
